package dijjer.io.xfer.http.handlers;

import java.io.IOException;
import java.io.Reader;
import java.net.ConnectException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.Map;
import dijjer.Dijjer;
import dijjer.io.xfer.http.HTTPException;
import dijjer.io.xfer.http.HTTPExceptionResponse;
import dijjer.io.xfer.http.HTTPResponse;
import dijjer.io.xfer.http.PageHandler;
import dijjer.util.Misc;
import dijjer.util.logging.Logger;

public class DownloadHandler extends PageHandler {

	public static final String VERSION = "$Id: DownloadHandler.java 2 2008-03-21 14:33:03Z ian.clarke $";
	public final static String CRLF = "\r\n";
	public static final int INITIAL_SNIFF_BYTES = 256;

	public HTTPResponse handleRequest(String method, String url, Map params, Map headers, Reader in) {
		try {
			url = url.substring(1);
			// Workaround for bug in some HTTP clients which convert '//' to '/' for some dumb reason
			if (url.toLowerCase().startsWith("http:/") && (url.charAt(6) != '/')) {
				url = "http://"+url.substring(6);
				Logger.info("Workaround wget and other dumb HTTP clients by correcting URL to  : "+url);
			}
			URL reqUrl = new URL(url);
			long contentLength = -1;
			HttpURLConnection uc2 = Misc.openHttpUrlConnection(reqUrl);
			uc2.setRequestMethod("HEAD");
            uc2.setRequestProperty("Referer", "http://dijjer.org/");
			uc2.setRequestProperty("Accept-Ranges", "bytes");
			try {
				uc2.connect();
//				if (uc2.getHeaderField("Location") != null) {
//					Logger.info("Redirecting from "+url+" to "+uc2.getHeaderField("Location"));
//					return handleRequest(method, uc2.getHeaderField("Location"), params, headers, in);
//				}
				contentLength = uc2.getContentLength();
			} catch (UnknownHostException e) {
				uc2.disconnect();
				return dumpError(HttpURLConnection.HTTP_NOT_FOUND, "Host not found",
						"Dijjer could not find the host specified (for contentLength detection)");
			} catch (ConnectException e) {
				uc2.disconnect();
				return dumpError(HttpURLConnection.HTTP_BAD_REQUEST, "Couldn't connect to host",
						"Diller could not connect to the host due to " + e.getMessage()
								+ "(for contentLength detection)");
			}
			uc2.disconnect();
			boolean range = false;
			long rangeStart = 0;
			long rangeEnd = contentLength - 1;
			if (headers.containsKey("range")) {
				String rawRange = (String) headers.get("range");
				System.err.println(rawRange);
				if (rawRange.startsWith("bytes=")) {
					String rs = rawRange.substring(6);
					int d = rs.indexOf("-");
					if (d != -1) {
						range = true;
						String bd = rs.substring(0, d);
						String ad = rs.substring(d + 1);
						if (!bd.equals("")) {
							rangeStart = Integer.parseInt(bd);
						}
						if (!ad.equals("")) {
							rangeEnd = Integer.parseInt(ad);
						}
					}
				}
			}
			int cycles = 0;
			HttpURLConnection uc;
			while (true) {
				cycles++;
				if (cycles > 5) {
					return dumpError(HttpURLConnection.HTTP_INTERNAL_ERROR, "Redirect loop detected",
							"There appears to be a redirect loop, aborting.");
				}
				uc = (HttpURLConnection) reqUrl.openConnection();
				uc.setRequestMethod("GET");
                uc.setRequestProperty("Referer", "http://www.dijjer.org/faq");
                uc.setRequestProperty("User-Agent", "Dijjer/1.0b/build-" + Dijjer.BUILD);
                uc.setRequestProperty("Dijjer-Version", "" + Dijjer.BUILD);
				// We only want to retrieve the initial sniff bytes
				uc.setRequestProperty("Range", "bytes=" + rangeStart + "-" + (INITIAL_SNIFF_BYTES + rangeStart - 1));
				Logger.info("Connecting to : " + reqUrl + " to request file information");
				try {
					uc.connect();
				} catch (UnknownHostException e) {
					return dumpError(HttpURLConnection.HTTP_NOT_FOUND, "Host not found",
							"Dijjer could not find the host specified.");
				} catch (ConnectException e) {
					return dumpError(HttpURLConnection.HTTP_BAD_REQUEST, "Couldn't connect to host",
							"Diller could not connect to the host due to " + e.getMessage());
				}
				String location = uc.getHeaderField("Location");
				if (location == null) {
					location = uc.getHeaderField("location");
				}
				if (location != null) {
					Logger.info("Redirected to " + location);
					reqUrl = new URL(location);
					continue;
				}
				if (uc.getResponseCode() == HttpURLConnection.HTTP_OK) {
					Logger.info("Failed to download " + reqUrl + " because host does not support partial downloads");
					return dumpError(HttpURLConnection.HTTP_NOT_IMPLEMENTED, "Partial download unsupported by host",
							"Dijjer can only download from servers that support "
									+ "partial downloads (the HTTP 1.1 Range header), and " + reqUrl.getHost()
									+ " does not.<p>You may request the file directly by clicking on " + "<a href=\""
									+ reqUrl + "\">" + reqUrl + "</a>.");
				}
				if (uc.getResponseCode() == HttpURLConnection.HTTP_PARTIAL) {
					break;
				} else {
					return dumpError(uc.getResponseCode(), uc.getResponseMessage(), "This error (code "
							+ uc.getResponseCode() + ") was returned to Dijjer by " + uc.getURL().getHost() + ".");
				}
			}
			// Respect caching headers
			HTTPResponse resp = cachingNotAllowed(uc, reqUrl);
			if (resp != null) {
				return resp;
			}
			byte[] initialSniffBytes = new byte[INITIAL_SNIFF_BYTES];
			uc.getInputStream().read(initialSniffBytes);
			int code = HttpURLConnection.HTTP_OK;
			if (range) {
				code = HttpURLConnection.HTTP_PARTIAL;
			}
			DownloadResponse response = new DownloadResponse(code, "OK", uc.getContentType(), contentLength,
					initialSniffBytes, reqUrl, Long.toString(uc.getLastModified()), rangeStart, rangeEnd);
			if (range) {
				response.addHeader("Content-Range", "bytes " + rangeStart + "-" + rangeEnd + "/" + contentLength);
			}
			return response;
		} catch (Exception e) {
			try {
				return new HTTPExceptionResponse(e);
			} catch (IOException ex) {
				throw new HTTPException(HttpURLConnection.HTTP_INTERNAL_ERROR, "Internal Error", ex.getMessage());
			}
		}
	}

	private HTTPResponse cachingNotAllowed(HttpURLConnection uc, URL reqUrl) throws IOException {
		String cc = uc.getHeaderField("Cache-Control");
		String pr = uc.getHeaderField("Pragma");
		if (((cc != null) && ((cc.indexOf("no-cache") != -1) || (cc.indexOf("private") != -1) || cc.indexOf("no-store") != -1))
				|| ((pr != null) && (uc.getHeaderField("Pragma").indexOf("no-cache") != -1))) {
			return dumpError(HttpURLConnection.HTTP_FORBIDDEN, "Caching is forbidden for this content",
					"The web server requested that this content not be cached.  "
							+ "You may download it directly from <a href=\"" + reqUrl + "\">" + reqUrl);
		}
		return null;
	}
}
